package ar.uade.fain.ai.tpo.dao;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import ar.uade.fain.ai.tpo.model.Producto;
import ar.uade.fain.ai.tpo.model.Seccion;

public class SeccionDAO extends CacheDAOSupport<Seccion> {

	private static final String DEREFERENCE_COMIDAS = 
			"UPDATE DELIVERY.DBO.COMIDA SET IDSECCION = NULL WHERE IDSECCION = ?";

	private static final String DELETE_STOCK = 
			"DELETE FROM DELIVERY.DBO.STOCK WHERE IDSECCION = ?";
	
	private static final String DELETE_SECCION = 
			//"DELETE FROM DELIVERY.DBO.SECCION WHERE IDSECCION = ?";
			"UPDATE DELIVERY.DBO.SECCION SET ACTIVO = 0 WHERE IDSECCION = ?";
			
	private static final String INSERT_SECCION = 
			"INSERT INTO DELIVERY.DBO.SECCION (IDSECCION, NOMBRE, ACTIVO) VALUES (?,?,1)";

	private static final String GET_BY_ID = 
			"SELECT SC.IDSECCION, SC.NOMBRE, ST.IDPRODUCTO, ST.CANTIDAD " +
			"FROM DELIVERY.DBO.SECCION SC " +
			"LEFT JOIN DELIVERY.DBO.STOCK ST ON SC.IDSECCION = ST.IDSECCION " +
			"WHERE SC.IDSECCION = ? AND SC.ACTIVO = 1";
	
	private static final String GET_NEXT_ID = 
			"SELECT ISNULL(MAX(IDSECCION),0)+1 AS ID FROM DELIVERY.DBO.SECCION";

	private static final String INSERT_STOCK = 
			"INSERT INTO DELIVERY.DBO.stock (IDSECCION, IDPRODUCTO, CANTIDAD) VALUES (?,?,?)";

	private static final String UPDATE_SECCION = 
			"UPDATE DELIVERY.DBO.SECCION SET NOMBRE = ? WHERE IDSECCION = ?";
	
	private static final String UPDATE_STOCK = 
			"UPDATE DELIVERY.DBO.stock CANTIDAD = ? WHERE IDSECCION = ?, IDPRODUCTO = ?";

	
	public SeccionDAO() {
		super();
	}
	
	public Seccion buscarPorId(final Long id) {
		Set<Seccion> enCache = buscarEnCache(new Selector<Seccion>() {
			@Override
			public boolean aceptar(Seccion entidad) {
				return entidad.getId().equals(id);
			}
		});
		Seccion result = null;
		if (enCache.isEmpty()) {
			result = load(id);
		} else {
			result = enCache.iterator().next();
		}
		return result;
	}

	private Seccion load(final Long id) {
		Seccion result;
		final List<Seccion> matches = database().select(GET_BY_ID, 
				id, new ResultSetExtractor<Seccion>() {
			@Override
			public List<Seccion> extraer(ResultSet rs) throws SQLException {
				ArrayList<Seccion> list = new ArrayList<Seccion>();
				if (rs.next()) {
					final Seccion seccion = new Seccion(
							rs.getLong("idseccion"), rs.getString("nombre"));
					long idproducto = rs.getLong("idproducto");
					if (!rs.wasNull()) {
						Producto producto = new Producto();
						producto.setId(idproducto);
						seccion.getStockProducto().agregar(
								producto, rs.getInt("cantidad"));
					}
					while (rs.next()) {
						Producto producto = new Producto();
						producto.setId(rs.getLong("idproducto"));
						seccion.getStockProducto().agregar(
								producto, rs.getInt("cantidad"));
					}
					list.add(seccion);
				}
				return list;
			}
		});
		result = matches.isEmpty() ? null : matches.get(0);
		agregarACache(result);
		return result;
	}
	
	public void guardar(final Seccion seccion) {
		final Long nextId = database().selectNextId(GET_NEXT_ID);
		database().insert(INSERT_SECCION, new ParameterSetter() {
			@Override
			public void setParametros(PreparedStatement statement) 
					throws SQLException {
				statement.setLong(1, nextId);
				statement.setString(2, seccion.getNombre());
			}
		});
		seccion.setId(nextId);
		insertStock(seccion);
		agregarACache(seccion);
	}

	private void insertStock(final Seccion seccion) {
		for (final Producto producto : 
			seccion.getStockProducto().getDisponibles().keySet()) {
			database().insert(INSERT_STOCK, new ParameterSetter() {
				@Override
				public void setParametros(PreparedStatement statement) 
						throws SQLException {
					statement.setLong(1, seccion.getId());
					statement.setLong(2, producto.getId());
					statement.setInt(3, seccion.getStockProducto()
							.getDisponibles().get(producto));
				}
			});
		}
	}
	
	public void eliminar(final Long idSeccion) {
		quitarDelCache(new Selector<Seccion>() {
			@Override
			public boolean aceptar(Seccion entidad) {
				return entidad.getId().equals(idSeccion);
			}
		});
		final ParameterSetter idSeccionSetter = 
				ParameterSetter.Factory.idOnly(idSeccion);
		database().update(DEREFERENCE_COMIDAS, idSeccionSetter);
		database().delete(DELETE_STOCK, idSeccionSetter);
		database().update(DELETE_SECCION, idSeccionSetter);
		
	}
	
	public void actualizar(final Seccion seccion) {
		database().update(UPDATE_SECCION, new ParameterSetter() {
			@Override
			public void setParametros(PreparedStatement statement) 
					throws SQLException {
				statement.setString(1, seccion.getNombre());
				statement.setLong(2, seccion.getId());
			}
		});
		database().delete(DELETE_STOCK, 
				ParameterSetter.Factory.idOnly(seccion.getId()));
		insertStock(seccion);
		agregarACache(seccion);
	}
	
	//TODO: Me parece que esta mal la logica.
	public void actualizarStock (final Seccion seccion, final Long idProducto, final int cantidad){
		database().update(UPDATE_STOCK, new ParameterSetter(){

			@Override
			public void setParametros(PreparedStatement statement)
					throws SQLException {
				statement.setLong(1, idProducto);
				statement.setInt(2, cantidad);
				statement.setLong(3, seccion.getId());	
			}
			//TODO: PREGUNTA COMO SE MODIFICA ESTO EN CACHE
		});
		
	}
	
	public int buscarCantidadDeUnProductoEnStock (final Long idSeccion, final Long idProducto){
		Seccion seccion = load(idSeccion);
		Integer cantidad = seccion.getStockProducto().getDisponibles().get(idProducto);
		return cantidad;
	}
	
}
